﻿using BootstrapBlazor.Components;
using System.ComponentModel;
using System.Net;
using System.Text;

namespace BlazorAdmin.Extensions
{
    /// <summary>
    /// 文件帮助类
    /// </summary>
    public class FileHelper
    {
        /// <summary>
        /// FTP下载文件方法
        /// </summary>        
        /// <param name="fileName">上传文件路径 + 文件名</param>
        /// <param name="errorMsg">错误信息</param>
        /// <returns></returns>
        public static (bool success, Stream fileStream, long fileLength) DownloadFtp(string fileName)
        {
            bool success = false;
            Stream fileStream;
            long fileLength;
            try
            {
                var uri = "ftp://10.3.7.83/" + fileName;
                var request = new WebClient();
                request.Credentials = new System.Net.NetworkCredential("ftpuser", "vsmftp2023");
                byte[] newFileData = request.DownloadData(uri);
                fileStream = new MemoryStream(newFileData);
                fileLength = newFileData.Length;
                success = true;
            }
            catch (Exception ex)
            {
                fileStream = null;
                fileLength = 0;
                success = false;
            }
            finally
            {
            }
            return (success, fileStream, fileLength);
        }

        public static async Task<(bool success, string fileNameOrErrMsg)> UploadFTP(UploadFile uploadFile, string savePath, EnumFileType? fileType = null, EnumFileSize? maxSize = null)
        {
            var errMsg = string.Empty;
            fileType = fileType ?? EnumFileType.Image; //默认上传图片，大小不超过10MB
            maxSize = maxSize ?? EnumFileSize.Small;

            int maxSizeMB = (int)maxSize;
            if (uploadFile.File.Size > maxSizeMB * 1024 * 1024)
                return (false, $"上传的文件不能超过{(int)maxSize}MB");

            string[] fileTypeList;
            if (fileType == EnumFileType.Video)
                fileTypeList = FileConst.FileTypVideo.Split(",", StringSplitOptions.RemoveEmptyEntries);
            else
                fileTypeList = FileConst.FileTypeImage.Split(",", StringSplitOptions.RemoveEmptyEntries);

            var fileExtension = uploadFile.File.Name.Split(".").Last().ToLower();
            if (fileTypeList.Contains(fileExtension))
                return (false, $"不支持的文件类型{fileExtension}\n请上传{string.Join("、", fileTypeList)}类型的文件");

            try
            {
                var ftpAddr = "ftp://10.3.7.83"; //ftp默认目录：var/ftp/vsm/
                var ftpUserID = "ftpuser";
                var ftpPassword = "vsmftp2023";

                Uri uri;
                var curDir = ftpAddr + "/";
                var dirArr = savePath.TrimStart('/').TrimEnd('/').Split('/');
                for (int i = 0; i < dirArr.Length; i++) //校验保存的目录是否存在，不存在则创建
                {
                    uri = new Uri(curDir);
                    if (!DirectoryExist(uri, ftpUserID, ftpPassword, dirArr[i]))
                    {
                        uri = new Uri(curDir + dirArr[i]);
                        MakeDir(ftpUserID, ftpPassword, uri);
                    }
                    curDir += dirArr[i] + "/";
                }

                var fileName = $"{Guid.NewGuid().ToString("N")}.{fileExtension}";
                uri = new Uri(curDir + fileName);

                var uploadRequest = CreateFtpWebRequest(ftpUserID, ftpPassword, uri, WebRequestMethods.Ftp.UploadFile);
                uploadRequest.ContentLength = uploadFile.File.Size;
                if (maxSizeMB > 100) //文件过大延长超时时间
                    uploadRequest.Timeout = 10 * 60 * 1000;

                using (var requestStream = uploadRequest.GetRequestStream()) //获得用于上传FTP的流  
                {
                    //注意：OpenReadStream 不支持同步方法CopyTo，只能 await
                    await uploadFile.File.OpenReadStream(maxSizeMB * 1024 * 1024).CopyToAsync(requestStream);
                }

                var uploadResponse = (FtpWebResponse)await uploadRequest.GetResponseAsync(); //返回FTP服务器响应，上传完成
                if (uploadResponse != null)
                {
                    uploadResponse.Close();

                    return (true, fileName);
                }
                else
                {
                    return (false, "上传失败，FTP服务器未响应");
                }
            }
            catch (Exception ex)
            {
                return (false, "上传失败\n" + ex.ToString());
            }
        }

        #region 内部方法

        private static bool DirectoryExist(Uri uri, string ftpUserID, string ftpPassword, string CreateDicName)
        {
            FtpWebRequest reqFTP = CreateFtpWebRequest(ftpUserID, ftpPassword, uri, WebRequestMethods.Ftp.ListDirectory);
            FtpWebResponse response = null;
            StreamReader reader = null;
            var isOK = false;
            try
            {
                response = (FtpWebResponse)reqFTP.GetResponse();
                reader = new StreamReader(response.GetResponseStream(), Encoding.UTF8);
                string line = reader.ReadLine();

                while (line != null)
                {
                    var lineStr = line.Split(' ');
                    if (lineStr[lineStr.Length - 1] == CreateDicName)
                    {
                        isOK = true;
                        break;
                    }

                    line = reader.ReadLine();
                }
            }
            catch (Exception ex)
            {
                return false;
            }
            finally
            {
                if (reader != null)
                    reader.Close();
                if (response != null)
                    response.Close();
            }

            return isOK;
        }

        private static bool MakeDir(string ftpUserID, string ftpPassword, Uri destinationPath)
        {
            FtpWebResponse response = null;
            try
            {
                Uri uri = new Uri(destinationPath.AbsoluteUri);
                FtpWebRequest reqFTP = CreateFtpWebRequest(ftpUserID, ftpPassword, uri, WebRequestMethods.Ftp.MakeDirectory);
                response = (FtpWebResponse)reqFTP.GetResponse();
                response.Close();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }

        private static FtpWebRequest CreateFtpWebRequest(string userName, string passWord, Uri uri, string method)
        {
            FtpWebRequest ftpClientRequest = (FtpWebRequest)WebRequest.Create(uri);
            ftpClientRequest.Method = method;
            ftpClientRequest.KeepAlive = false;
            ftpClientRequest.UseBinary = true;
            ftpClientRequest.UsePassive = true;
            ftpClientRequest.Proxy = null;
            ftpClientRequest.Credentials = new NetworkCredential(userName, passWord);//认证FTP用户名密码 ;                                                     
            return ftpClientRequest;
        }

        #endregion
    }


    public enum EnumFileType : byte
    {
        [Description("Video")]
        Video = 1,

        [Description("Image")]
        Image = 2
    }

    public enum EnumFileSize : int
    {
        [Description("500MB")]
        ExLarge = 500,

        [Description("200MB")]
        Large = 200,

        [Description("100MB")]
        Middle = 100,

        [Description("10MB")]
        Small = 10,
    }

    public class FileConst
    {
        public static readonly string FormatDate = "yyyy-MM-dd";

        public static readonly string FormatDateTime = "yyyy-MM-dd HH:mm:ss";

        public static readonly string ParkSubjectAuthSql = "@ParkSubjectAuthSql";

        //.mp4 .mkv .flv .mov .avi .wmv
        //video/mp4,video/x-matroska,video/x-flv,video/quicktime,video/x-msvideo,video/x-ms-wmv
        public static readonly string FileTypVideo = "video/mp4,video/x-matroska";

        public static readonly string FileTypeImage = "image/png,image/jpeg";

        /// <summary>
        /// {parkcode}/{subjectcode}/{coursecode}
        /// </summary>
        public static readonly string PathTrainCourse = "{0}/{1}/{2}";
    }
}
